﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;


namespace StarEF
{
    public class EFBaseRepository<T> : IEFBaseRepository<T> where T : class
    {
        protected DbContext dbEF;

        protected DbSet<T> DbSet;

        public EFBaseRepository(DbContext dbEF)
        {
            this.dbEF = dbEF;
            DbSet = dbEF.Set<T>();
        }

        public T Add(T entity)
        {
            return DbSet.Add(entity).Entity;
        }

        public async Task<T> AddAsync(T entity)
        {
            return (await DbSet.AddAsync(entity)).Entity;
        }

        public void BatchAdd(IEnumerable<T> entitys)
        {
            DbSet.AddRange(entitys);
        }

        public async Task BatchAddAsync(IEnumerable<T> entitys)
        {
            await DbSet.AddRangeAsync(entitys);
        }

        public void Delete(T entity)
        {
            if (entity != null)
            {
                if (dbEF.Entry(entity).State == EntityState.Detached)
                {
                    DbSet.Attach(entity);
                }

                DbSet.Remove(entity);
            }
        }

        public void Delete(object id)
        {
            T entity = DbSet.Find(id);
            Delete(entity);
        }

        public void Delete(Expression<Func<T, bool>> predicate)
        {
            foreach (T item in List(predicate))
            {
                Delete(item);
            }
        }

        public void Update(T entity)
        {
            DbSet.Attach(entity);
            dbEF.Entry(entity).State = EntityState.Modified;
        }

        public T Get(Expression<Func<T, bool>> predicate)
        {
            return dbEF.Set<T>().AsNoTracking().FirstOrDefault(predicate);
        }

        public async Task<T> GetAsync(Expression<Func<T, bool>> predicate)
        {
            return await dbEF.Set<T>().AsNoTracking().FirstOrDefaultAsync(predicate);
        }

        public async Task<T> GetAsync(object id)
        {
            return await dbEF.Set<T>().FindAsync(id);
        }

        public T Get(object id)
        {
            return dbEF.Set<T>().Find(id);
        }

        public bool Exist(Expression<Func<T, bool>> predicate)
        {
            return dbEF.Set<T>().Any(predicate);
        }

        public async Task<bool> ExistAsync(Expression<Func<T, bool>> predicate)
        {
            return await dbEF.Set<T>().AnyAsync(predicate);
        }

        public async Task<int> CountAsync(Expression<Func<T, bool>> predicate)
        {
            return await dbEF.Set<T>().CountAsync(predicate);
        }

        public int Count(Expression<Func<T, bool>> predicate)
        {
            return dbEF.Set<T>().Count(predicate);
        }

        public IQueryable<T> List(Expression<Func<T, bool>> predicate = null)
        {
            IQueryable<T> queryable = DbSet;
            if (predicate != null)
            {
                queryable = queryable.Where(predicate);
            }

            return queryable;
        }

        public IQueryable<T> List<S>(Expression<Func<T, bool>> whereLambda, bool isAsc, Expression<Func<T, S>> orderLambda, string includeProperties = "")
        {
            IQueryable<T> source = List(whereLambda);
            string[] array = includeProperties.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string navigationPropertyPath in array)
            {
                source = source.Include(navigationPropertyPath);
            }

            if (!isAsc)
            {
                return source.OrderByDescending(orderLambda);
            }

            return source.OrderBy(orderLambda);
        }

        public IQueryable<T> FindListBySSQL(string sql, params object[] parameters)
        {
            return DbSet.FromSqlRaw(sql, parameters);
        }

        public IQueryable<S> FromSqlRaw<S>(string sql, params object[] parameters) where S : class
        {
            return dbEF.Set<S>().FromSqlRaw(sql, parameters);
        }

        public int ExcuteBySQL(string sql, params object[] parameters)
        {
            return dbEF.Database.ExecuteSqlRaw(sql, parameters);
        }

        public async Task<int> ExcuteBySQLAsync(string sql, params object[] parameters)
        {
            return await dbEF.Database.ExecuteSqlRawAsync(sql, parameters);
        }

        public void Save()
        {
            dbEF.SaveChanges();
        }
    }
}